unit GenAI.RunSteps;

{-------------------------------------------------------------------------------

      Github repository :  https://github.com/MaxiDonkey/DelphiGenAI
      Visit the Github repository for the documentation and use examples

 ------------------------------------------------------------------------------}

interface

uses
  System.SysUtils, System.Classes, System.Threading, System.JSON, REST.Json.Types,
  REST.JsonReflect, System.Net.URLClient,
  GenAI.API.Params, GenAI.API, GenAI.Consts, GenAI.Types, GenAI.Async.Support,
  GenAI.API.Lists, GenAI.Assistants, GenAI.Runs;

type
  /// <summary>
  /// Represents URL parameters for retrieving specific run step details in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class is used to customize URL parameters when making requests to retrieve details about
  /// specific steps within an execution run. It enables including additional fields in the API response.
  /// </remarks>
  TRetrieveStepUrlParam = class(TUrlParam)
  public
    /// <summary>
    /// Specifies the additional fields to include in the API response when retrieving run step details.
    /// </summary>
    /// <param name="Value">
    /// An array of strings representing the fields to include in the response. For example, including
    /// the tool call results or additional step metadata.
    /// </param>
    /// <returns>
    /// Returns the current instance of <c>TRetrieveStepUrlParam</c>, allowing for method chaining.
    /// </returns>
    /// <remarks>
    /// The include parameter helps retrieve additional step details, such as file search results,
    /// tool outputs, or other relevant content within a step.
    /// </remarks>
    function Include(const Value: TArray<string>): TRetrieveStepUrlParam;
  end;

  /// <summary>
  /// Represents URL parameters for listing or retrieving multiple run steps in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class is used to customize URL parameters when making requests to list or retrieve details
  /// about multiple steps within an execution run. It allows for including additional data in the API response.
  /// </remarks>
  TRunStepUrlParam = class(TUrlAdvancedParams)
  public
    /// <summary>
    /// Specifies the additional fields to include in the API response when listing or retrieving run steps.
    /// </summary>
    /// <param name="Value">
    /// An array of strings representing the fields to include in the response, such as tool call outputs
    /// or file search results.
    /// </param>
    /// <returns>
    /// Returns the current instance of <c>TRunStepUrlParam</c>, enabling method chaining.
    /// </returns>
    /// <remarks>
    /// The include parameter allows retrieving extended information about the run steps, like intermediate
    /// tool outputs, search results, and any additional metadata attached to a step.
    /// </remarks>
    function Include(const Value: TArray<string>): TRunStepUrlParam;
  end;

  /// <summary>
  /// Represents details about the message creation step within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class provides information related to a message created during a run step, such as
  /// the unique identifier of the created message.
  /// </remarks>
  TRunStepMessageCreation = class
  private
    [JsonNameAttribute('message_id')]
    FMessageId: string;
  public
    /// <summary>
    /// Gets or sets the unique identifier of the message created during this run step.
    /// </summary>
    property MessageId: string read FMessageId write FMessageId;
  end;

  /// <summary>
  /// Represents details of an image output generated during a code interpreter run step in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class provides information about an image output, including the unique file identifier.
  /// </remarks>
  TOutputImage = class
  private
    [JsonNameAttribute('file_id')]
    FFileId: string;
  public
    /// <summary>
    /// Gets or sets the unique identifier of the file containing the image output.
    /// </summary>
    property FileId: string read FFileId write FFileId;
  end;

  /// <summary>
  /// Represents the output generated by the code interpreter during a run step in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class contains details about the output from the code interpreter, which can include logs
  /// and image outputs.
  /// </remarks>
  TCodeInterpreterOutput = class
  private
    FType: string;
    FLogs: string;
    FImage: TOutputImage;
  public
    /// <summary>
    /// Gets or sets the type of the output generated, such as logs or images.
    /// </summary>
    property &Type: string read FType write FType;
    /// <summary>
    /// Gets or sets the text-based logs generated during the code interpreter run step.
    /// </summary>
    property Logs: string read FLogs write FLogs;
    /// <summary>
    /// Gets or sets the image output generated by the code interpreter, if available.
    /// </summary>
    property Image: TOutputImage read FImage write FImage;
    destructor Destroy; override;
  end;

  /// <summary>
  /// Represents the details of a code interpreter step within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class contains information about the input provided to the code interpreter and the outputs
  /// it generated, such as logs or images.
  /// </remarks>
  TRunStepCodeInterpreter = class
  private
    FInput: string;
    FOutputs: TArray<TCodeInterpreterOutput>;
  public
    /// <summary>
    /// Gets or sets the input provided to the code interpreter during the run step.
    /// </summary>
    property Input: string read FInput write FInput;
    /// <summary>
    /// Gets or sets the outputs generated by the code interpreter, which may include logs and images.
    /// </summary>
    property Outputs: TArray<TCodeInterpreterOutput> read FOutputs write FOutputs;
    destructor Destroy; override;
  end;

  /// <summary>
  /// Represents the content of a search result within a file search tool call during an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class contains details about the content type and the corresponding text found during the file search.
  /// </remarks>
  TResultContent = class
  private
    FType: string;
    FText: string;
  public
    /// <summary>
    /// Gets or sets the type of the content, such as text or another supported format.
    /// </summary>
    property &Type: string read FType write FType;
    /// <summary>
    /// Gets or sets the textual content retrieved from the file search result.
    /// </summary>
    property Text: string read FText write FText;
  end;

  /// <summary>
  /// Represents a result from a file search tool call within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class contains information about a file search result, including the file details, score, and
  /// the content found within the file.
  /// </remarks>
  TFileSearchResult = class
  private
    [JsonNameAttribute('file_id')]
    FFileId: string;
    [JsonNameAttribute('file_name')]
    FFileName: string;
    FScore: Double;
    FContent: TArray<TResultContent>;
  public
    /// <summary>
    /// Gets or sets the unique identifier of the file where the search result was found.
    /// </summary>
    property FileId: string read FFileId write FFileId;
    /// <summary>
    /// Gets or sets the name of the file where the search result was found.
    /// </summary>
    property FileName: string read FFileName write FFileName;
    /// <summary>
    /// Gets or sets the score assigned to this search result, indicating its relevance.
    /// </summary>
    property Score: Double read FScore write FScore;
    /// <summary>
    /// Gets or sets the array of content results found in the file, such as matching text or data.
    /// </summary>
    property Content: TArray<TResultContent> read FContent write FContent;
    destructor Destroy; override;
  end;

  /// <summary>
  /// Represents details of a file search tool call within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class contains information about the file search operation, including the ranking options used
  /// and the results retrieved from the search.
  /// </remarks>
  TRunStepFileSearch = class
  private
    [JsonNameAttribute('ranking_options')]
    FRankingOptions: TRankingOptions;
    FResults: TArray<TFileSearchResult>;
  public
    /// <summary>
    /// Gets or sets the ranking options used to determine the relevance of search results.
    /// </summary>
    property RankingOptions: TRankingOptions read FRankingOptions write FRankingOptions;
    /// <summary>
    /// Gets or sets the array of search results, each containing details about the matching files and content.
    /// </summary>
    property Results: TArray<TFileSearchResult> read FResults write FResults;
    destructor Destroy; override;
  end;

  /// <summary>
  /// Represents details of a function tool call within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class contains information about a function tool call, including the function name,
  /// arguments passed, and the output generated.
  /// </remarks>
  TRunStepFunction = class
  private
    FName: string;
    FArguments: string;
    FOutput: string;
  public
    /// <summary>
    /// Gets or sets the name of the function called during this run step.
    /// </summary>
    property Name: string read FName write FName;
    /// <summary>
    /// Gets or sets the arguments provided to the function during the tool call.
    /// </summary>
    property Arguments: string read FArguments write FArguments;
    /// <summary>
    /// Gets or sets the output generated by the function after execution.
    /// </summary>
    property Output: string read FOutput write FOutput;
  end;

  /// <summary>
  /// Represents details of tool calls made during a specific run step in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class provides information about various tool calls, such as code interpreter executions,
  /// file searches, or function invocations.
  /// </remarks>
  TRunStepToolCalls = class
  private
    FId: string;
    [JsonReflectAttribute(ctString, rtString, TAssistantsToolsTypeInterceptor)]
    FType: TAssistantsToolsType;
    [JsonNameAttribute('code_interpreter')]
    FCodeInterpreter: TRunStepCodeInterpreter;
    [JsonNameAttribute('file_search')]
    FFileSearch: TRunStepFileSearch;
    FFunction: TRunStepFunction;
  public
    /// <summary>
    /// Gets or sets the unique identifier of the tool call.
    /// </summary>
    property Id: string read FId write FId;
    /// <summary>
    /// Gets or sets the type of the tool call, such as a code interpreter, file search, or function.
    /// </summary>
    property &Type: TAssistantsToolsType read FType write FType;
    /// <summary>
    /// Gets or sets the details of the code interpreter tool call if applicable.
    /// </summary>
    property CodeInterpreter: TRunStepCodeInterpreter read FCodeInterpreter write FCodeInterpreter;
    /// <summary>
    /// Gets or sets the details of the file search tool call if applicable.
    /// </summary>
    property FileSearch: TRunStepFileSearch read FFileSearch write FFileSearch;
    /// <summary>
    /// Gets or sets the details of the function tool call if applicable.
    /// </summary>
    property &Function: TRunStepFunction read FFunction write FFunction;
    destructor Destroy; override;
  end;

  /// <summary>
  /// Represents the detailed information of a run step within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class provides details about the type of run step and any associated tool calls
  /// or message creation activities.
  /// </remarks>
  TRunStepDetails = class
  private
    [JsonReflectAttribute(ctString, rtString, TRunStepTypeInterceptor)]
    FType: TRunStepType;
    [JsonNameAttribute('message_creation')]
    FMessageCreation: TRunStepMessageCreation;
    [JsonNameAttribute('tool_calls')]
    FToolCalls: TArray<TRunStepToolCalls>;
  public
    /// <summary>
    /// Gets or sets the type of the run step, such as message creation or tool calls.
    /// </summary>
    property &Type: TRunStepType read FType write FType;
    /// <summary>
    /// Gets or sets the details of a message created during this run step, if applicable.
    /// </summary>
    property MessageCreation: TRunStepMessageCreation read FMessageCreation write FMessageCreation;
    /// <summary>
    /// Gets or sets the array of tool calls made during this run step, if any.
    /// </summary>
    property ToolCalls: TArray<TRunStepToolCalls> read FToolCalls write FToolCalls;
    destructor Destroy; override;
  end;

  TRunStepTimestamp = class(TJSONFingerprint)
  protected
    function GetCreatedAtAsString: string; virtual; abstract;
    function GetExpiredAtAsString: string; virtual; abstract;
    function GetCancelledAtAsString: string; virtual; abstract;
    function GetFailedAtAsString: string; virtual; abstract;
    function GetCompletedAtAsString: string; virtual; abstract;
  public
    property CreatedAtAsString: string read GetCreatedAtAsString;
    property ExpiredAtAsString: string read GetExpiredAtAsString;
    property CancelledAtAsString: string read GetCancelledAtAsString;
    property FailedAtAsString: string read GetFailedAtAsString;
    property CompletedAtAsString: string read GetCompletedAtAsString;
  end;

  /// <summary>
  /// Represents a specific step within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class contains details about the run step, such as its type, status, associated assistant,
  /// and any outputs or errors generated during the step.
  /// </remarks>
  TRunStep = class(TRunStepTimestamp)
  private
    FId: string;
    FObject: string;
    [JsonNameAttribute('created_at')]
    FCreatedAt: TInt64OrNull;
    [JsonNameAttribute('assistant_id')]
    FAssistantId: string;
    [JsonNameAttribute('thread_id')]
    FThreadId: string;
    [JsonNameAttribute('run_id')]
    FRunId: string;
    [JsonReflectAttribute(ctString, rtString, TRunStepTypeInterceptor)]
    FType: TRunStepType;
    [JsonReflectAttribute(ctString, rtString, TRunStatusInterceptor)]
    FStatus: TRunStatus;
    [JsonNameAttribute('step_details')]
    FStepDetails: TRunStepDetails;
    [JsonNameAttribute('last_error')]
    FLastError: TLastError;
    [JsonNameAttribute('expired_at')]
    FExpiredAt: TInt64OrNull;
    [JsonNameAttribute('cancelled_at')]
    FCancelledAt: TInt64OrNull;
    [JsonNameAttribute('failed_at')]
    FFailedAt: TInt64OrNull;
    [JsonNameAttribute('completed_at')]
    FCompletedAt: TInt64OrNull;
    [JsonReflectAttribute(ctString, rtString, TMetadataInterceptor)]
    FMetadata: string;
    FUsage: TRunUsage;
    function GetCreatedAt: Int64;
    function GetExpiredAt: Int64;
    function GetCancelledAt: Int64;
    function GetFailedAt: Int64;
    function GetCompletedAt: Int64;
  protected
    function GetCreatedAtAsString: string; override;
    function GetExpiredAtAsString: string; override;
    function GetCancelledAtAsString: string; override;
    function GetFailedAtAsString: string; override;
    function GetCompletedAtAsString: string; override;
  public
    /// <summary>
    /// Gets or sets the unique identifier of the run step.
    /// </summary>
    property Id: string read FId write FId;
    /// <summary>
    /// Gets or sets the object type, which is always "thread.run.step".
    /// </summary>
    property &Object: string read FObject write FObject;
    /// <summary>
    /// Gets the creation timestamp of the run step.
    /// </summary>
    property CreatedAt: Int64 read GetCreatedAt;
    /// <summary>
    /// Gets or sets the ID of the assistant associated with the run step.
    /// </summary>
    property AssistantId: string read FAssistantId write FAssistantId;
    /// <summary>
    /// Gets or sets the ID of the thread to which the run step belongs.
    /// </summary>
    property ThreadId: string read FThreadId write FThreadId;
    /// <summary>
    /// Gets or sets the ID of the execution run associated with this step.
    /// </summary>
    property RunId: string read FRunId write FRunId;
    /// <summary>
    /// Gets or sets the type of the run step, such as message creation or tool calls.
    /// </summary>
    property &Type: TRunStepType read FType write FType;
    /// <summary>
    /// Gets or sets the status of the run step, which can be in progress, completed, failed, or cancelled.
    /// </summary>
    property Status: TRunStatus read FStatus write FStatus;
    /// <summary>
    /// Gets or sets detailed information about the run step, including tool calls or message creation.
    /// </summary>
    property StepDetails: TRunStepDetails read FStepDetails write FStepDetails;
    /// <summary>
    /// Gets or sets details about the last error encountered during the run step, if any.
    /// </summary>
    property LastError: TLastError read FLastError write FLastError;
    /// <summary>
    /// Gets the timestamp when the run step expired, if applicable.
    /// </summary>
    property ExpiredAt: Int64 read GetExpiredAt;
    /// <summary>
    /// Gets the timestamp when the run step was cancelled, if applicable.
    /// </summary>
    property CancelledAt: Int64 read GetCancelledAt;
    /// <summary>
    /// Gets the timestamp when the run step failed, if applicable.
    /// </summary>
    property FailedAt: Int64 read GetFailedAt;
    /// <summary>
    /// Gets the timestamp when the run step was completed, if applicable.
    /// </summary>
    property CompletedAt: Int64 read GetCompletedAt;
    /// <summary>
    /// Gets or sets any metadata associated with the run step.
    /// </summary>
    property Metadata: string read FMetadata write FMetadata;
    /// <summary>
    /// Gets or sets usage statistics related to token consumption during the run step.
    /// </summary>
    property Usage: TRunUsage read FUsage write FUsage;
    destructor Destroy; override;
  end;

  /// <summary>
  /// Represents a collection of run steps within an execution run in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class is a list of <c>TRunStep</c> objects, providing access to multiple steps within a run.
  /// It allows for iteration over the run steps to retrieve their details, such as outputs, statuses, or errors.
  /// </remarks>
  TRunSteps = TAdvancedList<TRunStep>;

  /// <summary>
  /// Manages asynchronous callBacks for a request using <c>TRunStep</c> as the response type.
  /// </summary>
  /// <remarks>
  /// The <c>TAsynRunStep</c> type extends the <c>TAsynParams&lt;TRunStep&gt;</c> record to handle the lifecycle of an asynchronous chat operation.
  /// It provides event handlers that trigger at various stages, such as when the operation starts, completes successfully, or encounters an error.
  /// This structure facilitates non-blocking operations.
  /// </remarks>
  TAsynRunStep = TAsynCallBack<TRunStep>;

  /// <summary>
  /// Manages asynchronous callBacks for a request using <c>TRunSteps</c> as the response type.
  /// </summary>
  /// <remarks>
  /// The <c>TAsynRunSteps</c> type extends the <c>TAsynParams&lt;TRunSteps&gt;</c> record to handle the lifecycle of an asynchronous chat operation.
  /// It provides event handlers that trigger at various stages, such as when the operation starts, completes successfully, or encounters an error.
  /// This structure facilitates non-blocking operations.
  /// </remarks>
  TAsynRunSteps = TAsynCallBack<TRunSteps>;

  /// <summary>
  /// Represents the route for managing run steps within execution runs in the OpenAI API.
  /// </summary>
  /// <remarks>
  /// This class provides methods to list or retrieve details of run steps. It handles both synchronous
  /// and asynchronous requests, enabling efficient interaction with the OpenAI API for managing run steps.
  /// </remarks>
  TRunStepRoute = class(TGenAIRoute)
  protected
    procedure HeaderCustomize; override;
  public
    /// <summary>
    /// Asynchronously retrieves a list of run steps associated with an execution run.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run whose steps are to be listed.</param>
    /// <param name="CallBacks">Callback functions to handle the asynchronous execution.</param>
    procedure AsynList(const ThreadId: string;
      const RunId: string;
      const CallBacks: TFunc<TAsynRunSteps>); overload;
    /// <summary>
    /// Asynchronously retrieves a filtered list of run steps based on specified parameters.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run whose steps are to be listed.</param>
    /// <param name="ParamProc">A procedure specifying the filter parameters for the list.</param>
    /// <param name="CallBacks">Callback functions to handle the asynchronous execution.</param>
    procedure AsynList(const ThreadId: string;
      const RunId: string;
      const ParamProc: TProc<TRunStepUrlParam>;
      const CallBacks: TFunc<TAsynRunSteps>); overload;
    /// <summary>
    /// Asynchronously retrieves details of a specific run step.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run to which the step belongs.</param>
    /// <param name="StepId">The ID of the run step to retrieve.</param>
    /// <param name="CallBacks">Callback functions to handle the asynchronous execution.</param>
    procedure AsynRetrieve(const ThreadId: string;
      const RunId: string;
      const StepId: string;
      const CallBacks: TFunc<TAsynRunStep>); overload;
    /// <summary>
    /// Asynchronously retrieves details of a specific run step with additional options.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run to which the step belongs.</param>
    /// <param name="StepId">The ID of the run step to retrieve.</param>
    /// <param name="ParamProc">A procedure specifying additional options for the retrieval.</param>
    /// <param name="CallBacks">Callback functions to handle the asynchronous execution.</param>
    procedure AsynRetrieve(const ThreadId: string;
      const RunId: string;
      const StepId: string;
      const ParamProc: TProc<TRetrieveStepUrlParam>;
      const CallBacks: TFunc<TAsynRunStep>); overload;
    /// <summary>
    /// Retrieves a list of run steps associated with a specific execution run.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run whose steps are to be listed.</param>
    /// <returns>A list of <c>TRunStep</c> objects representing the steps within the run.</returns>
    function List(const ThreadId: string; const RunId: string): TRunSteps; overload;
    /// <summary>
    /// Retrieves a filtered list of run steps based on specified parameters.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run whose steps are to be listed.</param>
    /// <param name="ParamProc">A procedure specifying the filter parameters for the list.</param>
    /// <returns>A list of <c>TRunStep</c> objects representing the filtered steps.</returns>
    function List(const ThreadId: string; const RunId: string;
      const ParamProc: TProc<TRunStepUrlParam>): TRunSteps; overload;
    /// <summary>
    /// Retrieves details of a specific run step within an execution run.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run to which the step belongs.</param>
    /// <param name="StepId">The ID of the run step to retrieve.</param>
    /// <returns>A <c>TRunStep</c> object containing the details of the specified run step.</returns>
    function Retrieve(const ThreadId: string; const RunId: string;
      const StepId: string): TRunStep; overload;
    /// <summary>
    /// Retrieves details of a specific run step with additional options.
    /// </summary>
    /// <param name="ThreadId">The ID of the thread containing the run.</param>
    /// <param name="RunId">The ID of the run to which the step belongs.</param>
    /// <param name="StepId">The ID of the run step to retrieve.</param>
    /// <param name="ParamProc">A procedure specifying additional options for the retrieval.</param>
    /// <returns>A <c>TRunStep</c> object containing the details of the specified run step.</returns>
    function Retrieve(const ThreadId: string; const RunId: string;
      const StepId: string; const ParamProc: TProc<TRetrieveStepUrlParam>): TRunStep; overload;
  end;

implementation

{ TRetrieveStepUrlParam }

function TRetrieveStepUrlParam.Include(
  const Value: TArray<string>): TRetrieveStepUrlParam;
begin
  Result := TRetrieveStepUrlParam(Add('include', Value));
end;

{ TRunStepUrlParam }

function TRunStepUrlParam.Include(
  const Value: TArray<string>): TRunStepUrlParam;
begin
  Result := TRunStepUrlParam(Add('include', Value));
end;

{ TRunStep }

destructor TRunStep.Destroy;
begin
  if Assigned(FStepDetails) then
    FStepDetails.Free;
  if Assigned(FLastError) then
    FLastError.Free;
  if Assigned(FUsage) then
    FUsage.Free;
  inherited;
end;

function TRunStep.GetCancelledAt: Int64;
begin
  Result := TInt64OrNull(FCancelledAt).ToInteger;
end;

function TRunStep.GetCancelledAtAsString: string;
begin
  Result := TInt64OrNull(FCancelledAt).ToUtcDateString;
end;

function TRunStep.GetCompletedAt: Int64;
begin
  Result := TInt64OrNull(CompletedAt).ToInteger;
end;

function TRunStep.GetCompletedAtAsString: string;
begin
  Result := TInt64OrNull(FCompletedAt).ToUtcDateString;
end;

function TRunStep.GetCreatedAt: Int64;
begin
  Result := TInt64OrNull(FCreatedAt).ToInteger;
end;

function TRunStep.GetCreatedAtAsString: string;
begin
  Result := TInt64OrNull(FCreatedAt).ToUtcDateString;
end;

function TRunStep.GetExpiredAt: Int64;
begin
  Result := TInt64OrNull(FExpiredAt).ToInteger;
end;

function TRunStep.GetExpiredAtAsString: string;
begin
  Result := TInt64OrNull(FExpiredAt).ToUtcDateString;
end;

function TRunStep.GetFailedAt: Int64;
begin
  Result := TInt64OrNull(FFailedAt).ToInteger;
end;

function TRunStep.GetFailedAtAsString: string;
begin
  Result := TInt64OrNull(FFailedAt).ToUtcDateString;
end;

{ TRunStepDetails }

destructor TRunStepDetails.Destroy;
begin
  if Assigned(FMessageCreation) then
    FMessageCreation.Free;
  for var Item in FToolCalls do
    Item.Free;
  inherited;
end;

{ TRunStepToolCalls }

destructor TRunStepToolCalls.Destroy;
begin
  if Assigned(FCodeInterpreter) then
    FCodeInterpreter.Free;
  if Assigned(FFileSearch) then
    FFileSearch.Free;
  if Assigned(FFunction) then
    FFunction.Free;
  inherited;
end;

{ TRunStepCodeInterpreter }

destructor TRunStepCodeInterpreter.Destroy;
begin
  for var Item in FOutputs do
    Item.Free;
  inherited;
end;

{ TCodeInterpreterOutput }

destructor TCodeInterpreterOutput.Destroy;
begin
  if Assigned(FImage) then
    FImage.Free;
  inherited;
end;

{ TRunStepFileSearch }

destructor TRunStepFileSearch.Destroy;
begin
  if Assigned(FRankingOptions) then
    FRankingOptions.Free;
  for var Item in FResults do
    Item.Free;
  inherited;
end;

{ TFileSearchResult }

destructor TFileSearchResult.Destroy;
begin
  for var Item in FContent do
    Item.Free;
  inherited;
end;

{ TRunStepRoute }

procedure TRunStepRoute.AsynList(const ThreadId, RunId: string;
  const CallBacks: TFunc<TAsynRunSteps>);
begin
  with TAsynCallBackExec<TAsynRunSteps, TRunSteps>.Create(CallBacks) do
  try
    Sender := Use.Param.Sender;
    OnStart := Use.Param.OnStart;
    OnSuccess := Use.Param.OnSuccess;
    OnError := Use.Param.OnError;
    Run(
      function: TRunSteps
      begin
        Result := Self.List(ThreadId, RunId);
      end);
  finally
    Free;
  end;
end;

procedure TRunStepRoute.AsynList(const ThreadId, RunId: string;
  const ParamProc: TProc<TRunStepUrlParam>;
  const CallBacks: TFunc<TAsynRunSteps>);
begin
  with TAsynCallBackExec<TAsynRunSteps, TRunSteps>.Create(CallBacks) do
  try
    Sender := Use.Param.Sender;
    OnStart := Use.Param.OnStart;
    OnSuccess := Use.Param.OnSuccess;
    OnError := Use.Param.OnError;
    Run(
      function: TRunSteps
      begin
        Result := Self.List(ThreadId, RunId, ParamProc);
      end);
  finally
    Free;
  end;
end;

procedure TRunStepRoute.AsynRetrieve(const ThreadId, RunId, StepId: string;
  const ParamProc: TProc<TRetrieveStepUrlParam>;
  const CallBacks: TFunc<TAsynRunStep>);
begin
  with TAsynCallBackExec<TAsynRunStep, TRunStep>.Create(CallBacks) do
  try
    Sender := Use.Param.Sender;
    OnStart := Use.Param.OnStart;
    OnSuccess := Use.Param.OnSuccess;
    OnError := Use.Param.OnError;
    Run(
      function: TRunStep
      begin
        Result := Self.Retrieve(ThreadId, RunId, StepId, ParamProc);
      end);
  finally
    Free;
  end;
end;

procedure TRunStepRoute.AsynRetrieve(const ThreadId, RunId, StepId: string;
  const CallBacks: TFunc<TAsynRunStep>);
begin
  with TAsynCallBackExec<TAsynRunStep, TRunStep>.Create(CallBacks) do
  try
    Sender := Use.Param.Sender;
    OnStart := Use.Param.OnStart;
    OnSuccess := Use.Param.OnSuccess;
    OnError := Use.Param.OnError;
    Run(
      function: TRunStep
      begin
        Result := Self.Retrieve(ThreadId, RunId, StepId);
      end);
  finally
    Free;
  end;
end;

procedure TRunStepRoute.HeaderCustomize;
begin
  inherited;
  API.CustomHeaders := [TNetHeader.Create('OpenAI-Beta', 'assistants=v2')];
end;

function TRunStepRoute.List(const ThreadId, RunId: string;
  const ParamProc: TProc<TRunStepUrlParam>): TRunSteps;
begin
  HeaderCustomize;
  Result := API.Get<TRunSteps, TRunStepUrlParam>('threads/' + ThreadId + '/runs/' + RunId + '/steps', ParamProc);
end;

function TRunStepRoute.List(const ThreadId, RunId: string): TRunSteps;
begin
  HeaderCustomize;
  Result := API.Get<TRunSteps>('threads/' + ThreadId + '/runs/' + RunId + '/steps');
end;

function TRunStepRoute.Retrieve(const ThreadId, RunId,
  StepId: string): TRunStep;
begin
  HeaderCustomize;
  Result := API.Get<TRunStep>('threads/' + ThreadId + '/runs/' + RunId + '/steps/' + StepId);
end;

function TRunStepRoute.Retrieve(const ThreadId, RunId, StepId: string;
  const ParamProc: TProc<TRetrieveStepUrlParam>): TRunStep;
begin
  HeaderCustomize;
  Result := API.Get<TRunStep, TRetrieveStepUrlParam>('threads/' + ThreadId + '/runs/' + RunId + '/steps/' + StepId, ParamProc);
end;

end.
